package com.zjmzxfzhl.common.util;


import org.springframework.format.annotation.DateTimeFormat;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.MessageFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Created by yangjf on 2018/2/28.
 */
public class LocalDateUtil {

    private static DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static DateTimeFormatter TIME_DF = DateTimeFormatter.ofPattern("HH:mm:ss");

    public static LocalDate parse2LocalDate(String date) {
        try {
            return LocalDate.parse(date);
        } catch (Exception e) {
            throw new RuntimeException("处理日期错误，检查日期是否合法：" + date);
        }
    }

    /**
     * 校验时间字符串并返回 localDateTime
     *
     * @param dateTimeStr yyyy-MM-dd HH:mm:ss 字符串
     * @return
     */
    public static LocalDateTime parse2LocalDateTime(String dateTimeStr) {
        try {
            return LocalDateTime.parse(dateTimeStr, df);
        } catch (Exception ex) {
            throw new RuntimeException(MessageFormat.format("时间字符串{0}格式错误，参考 yyyy-MM-dd HH:mm:ss", dateTimeStr));
        }
    }

    /**
     * 截取日期
     *
     * @param date
     * @return
     */
    public static String cutDate(String date) {
        return date.substring(0, 10);
    }

    /**
     * 截取月份
     *
     * @param date
     * @return
     */
    public static String cutMonth(String date) {
        return date.substring(0, 7);
    }

    public static String cutTime(String dateTimeStr) {
        return dateTimeStr.substring(11, 19);
    }

    public static LocalDate getStartDate(String startMonth) {
        try {
            String startDate = startMonth + "-01";
            return LocalDate.parse(startDate);
        } catch (Exception e) {
            throw new RuntimeException("处理日期错误，检查月份是否合法：" + startMonth);
        }
    }

    public static LocalDate getEndDate(String endMonth) {
        LocalDate tempLocalDate = getStartDate(endMonth);
        return tempLocalDate.plus(1, ChronoUnit.MONTHS);
    }


    /**
     * 检查日期格式 ，如果正确返回 localDate;
     *
     * @param date
     * @return
     */
    public static LocalDate checkDate(String date) {
        try {
            LocalDate localDate = LocalDate.parse(date);
            return localDate;
        } catch (Exception ex) {
            throw new RuntimeException("时间格式（yyyy-MM-dd）错误：" + date);
        }
    }

    /**
     * 收集起始时间到结束时间之间所有的时间并以字符串集合方式返回
     *
     * @param start "2017-02-01"
     * @param end   "2017-03-01"
     * @return ["2017-02-01","2017-02-02","2017-02-03"...,"2017-02-27","2017-02-28"]
     */
    public static List<String> collectLocalDateStrings(LocalDate start, LocalDate end, boolean containsEnd) {
        if (end.isBefore(start)) {
            throw new RuntimeException("结束日期需要大于开始日期");
        }
        long days = ChronoUnit.DAYS.between(start, end);
        if (containsEnd) {
            days += 1;
        }
        // 用起始时间作为流的源头，按照每次加一天的方式创建一个无限流
        return Stream.iterate(start, localDate -> localDate.plusDays(1))
                // 截断无限流，长度为起始时间和结束时间的差
                .limit(days)
                // 由于最后要的是字符串，所以map转换一下
                .map(LocalDate::toString)
                // 把流收集为List
                .collect(Collectors.toList());
    }

    public static List<LocalDate> collectLocalDates(LocalDate start, LocalDate end, boolean containsEnd) {
        long days = ChronoUnit.DAYS.between(start, end);
        if (containsEnd) {
            days += 1;
        }
        // 用起始时间作为流的源头，按照每次加一天的方式创建一个无限流
        return Stream.iterate(start, localDate -> localDate.plusDays(1))
                // 截断无限流，长度为起始时间和结束时间的差
                .limit(days)
                // 把流收集为List
                .collect(Collectors.toList());
    }

    public static List<String> collectMonths(String startDate, String endDate, boolean containsEnd) {
        LocalDate startLocalDate = checkDate(startDate);
        LocalDate endLocalDate = checkDate(endDate);
        return collectLocalMonths(startLocalDate, endLocalDate, containsEnd);
    }

    public static List<String> collectLocalMonths(LocalDate start, LocalDate end, boolean containsEnd) {
        long months = ChronoUnit.MONTHS.between(start, end);
        if (!containsEnd) {
            months -= 1;
        }
        // 用起始时间作为流的源头，按照每次加一天的方式创建一个无限流
        return Stream.iterate(start, localDate -> localDate.plusMonths(1))
                // 截断无限流，长度为起始时间和结束时间的差
                .limit(months)
                // 由于最后要的是字符串，所以map转换一下
                .map(e -> cutMonth(e.toString()))
                // 把流收集为List
                .collect(Collectors.toList());
    }

    public static List<String> collectLocalMonths(String startMonth, String endMonth, boolean containsEnd) {
        LocalDate startDate = getStartDate(startMonth);
        LocalDate endDate = getEndDate(endMonth);
        return collectLocalMonths(startDate, endDate, containsEnd);
    }

    public static int calcLeftDays(LocalDate startDate, LocalDate endDate) {
        LocalDate now = LocalDate.now();
        if (now.isAfter(endDate) || now.isEqual(endDate)) {
            //当前日期大于等于结束日期，剩余0天
            return 0;
        } else if (now.isAfter(startDate) || now.isEqual(startDate)) {
            //当前日期在 开始时间和结束时间之间
            return (int) (now.until(endDate, ChronoUnit.DAYS) - 1);
        } else {
            //当前日期在开始日期之前  (now.isBefore(startDate))
            return (int) startDate.until(endDate, ChronoUnit.DAYS);
        }
    }

    /**
     * @param date "yyyy-MM-dd"
     * @return yyyy-MM-dd 00:00:00 的timestamp
     */
    public static Timestamp getStartTimestamp(String date) {
        try {
            LocalDateTime localDateTime = LocalDate.parse(date).atStartOfDay();
            return Timestamp.valueOf(localDateTime);
        } catch (Exception ex) {
            throw new RuntimeException("时间格式（yyyy-MM-dd）错误：" + date);
        }
    }

    /**
     * 将日期字符串填 当天开始时间
     *
     * @param date
     * @return
     */
    public static String getStartDateTime(String date) {
        try {
            LocalDateTime localDateTime = LocalDate.parse(date).atStartOfDay();
            return df.format(localDateTime);
        } catch (Exception ex) {
            throw new RuntimeException("时间格式（yyyy-MM-dd）错误：" + date);
        }
    }

    public static String getTimeFromTimestamp(Timestamp timestamp) {
        try {
            return timestamp.toLocalDateTime().toLocalTime().format(TIME_DF);
        } catch (Exception ex) {
            throw new RuntimeException("时间转换错误");
        }
    }

    /**
     * 将时间字符串填充 当前日期
     *
     * @param timeStr
     * @return
     */
    public static String getCurrentTimeStrFrom(String timeStr) {
        try {
            LocalDate now = LocalDate.now();
            LocalDateTime localDateTime = LocalTime.parse(timeStr).atDate(now);
            return df.format(localDateTime);
        } catch (Exception ex) {
            throw new RuntimeException("时间格式（HH:mm:ss）错误：" + timeStr);
        }
    }

    /**
     * 将时间字符串填充 当前日期
     *
     * @param timeStr HH:mm:ss
     * @return
     */
    public static LocalTime getCurrentLocalTime(String timeStr) {
        try {
            LocalTime localTime = LocalTime.parse(timeStr);
            return localTime;
        } catch (Exception ex) {
            throw new RuntimeException("时间格式（HH:mm:ss）错误：" + timeStr);
        }
    }


    public static String getCurrentTimeStrFrom(LocalTime localTime) {
        try {
            LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), localTime);
            return df.format(localDateTime);
        } catch (Exception ex) {
            throw new RuntimeException("时间转换错误：" + localTime.toString());
        }
    }

    public static String getNextDayTimeStrFrom(LocalTime localTime) {
        try {
            LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now().plusDays(1), localTime);
            return df.format(localDateTime);
        } catch (Exception ex) {
            throw new RuntimeException("时间转换错误：" + localTime.toString());
        }
    }

    /**
     * @param timeStr HH:mm:ss
     * @return 以当天为日期的 timestamp
     */
    public static Timestamp getTimestampFrom(String timeStr) {
        try {
            LocalDate now = LocalDate.now();
            LocalDateTime localDateTime = LocalTime.parse(timeStr).atDate(now);
            return Timestamp.valueOf(localDateTime);
        } catch (Exception ex) {
            throw new RuntimeException("时间格式（HH:mm:ss）错误：" + timeStr);
        }

    }

    /**
     * 日期相隔天数
     *
     * @param startDateInclusive
     * @param endDateExclusive
     * @return
     */
    public static BigDecimal periodDays(LocalDateTime startDateInclusive, LocalDateTime endDateExclusive) {
        try {
            if (endDateExclusive.isBefore(startDateInclusive)) {
                throw new RuntimeException("结束日期需要大于开始日期");
            }
            long endTime = endDateExclusive.toEpochSecond(ZoneOffset.of("+8"));
            long startTime = startDateInclusive.toEpochSecond(ZoneOffset.of("+8"));
            BigDecimal timeDiff = new BigDecimal(endTime - startTime);
            BigDecimal oneDayTime = new BigDecimal(3600 * 24);
            BigDecimal result = timeDiff.divide(oneDayTime, 4, BigDecimal.ROUND_DOWN);
//            BigDecimal result = new BigDecimal(endTime - startTime).divide(new BigDecimal (3600 * 24)).setScale(4, BigDecimal.ROUND_DOWN);
            return result;
        } catch (Exception e) {
            throw new RuntimeException("时间转换错误");
        }
    }

    /**
     * 获取一个随机日期，在
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static LocalDate randomDate(LocalDate startDate, LocalDate endDate) {
        if (endDate.isBefore(startDate)) {
            throw new RuntimeException("开始日期必须小于等于结束日期");
        }
        long days = ChronoUnit.DAYS.between(startDate, endDate);
        int ra = (int) (Math.random() * (1 + days));
        return startDate.plusDays(ra);
    }

    /**
     * 日期相隔天数
     *
     * @param startDateInclusive
     * @param endDateExclusive
     * @return
     */
    public static BigDecimal periodHours(LocalDateTime startDateInclusive, LocalDateTime endDateExclusive) {
        try {
            if (endDateExclusive.isBefore(startDateInclusive)) {
                throw new RuntimeException("结束日期需要大于开始日期");
            }
            long endTime = endDateExclusive.toEpochSecond(ZoneOffset.of("+8"));
            long startTime = startDateInclusive.toEpochSecond(ZoneOffset.of("+8"));
            BigDecimal timeDiff = new BigDecimal(endTime - startTime);
            BigDecimal oneHourTime = new BigDecimal(3600);
            BigDecimal result = timeDiff.divide(oneHourTime, 4, BigDecimal.ROUND_DOWN);
//            BigDecimal result = new BigDecimal(endTime - startTime).divide(new BigDecimal (3600 * 24)).setScale(4, BigDecimal.ROUND_DOWN);
            return result;
        } catch (Exception e) {
            throw new RuntimeException("时间转换错误");
        }
    }
}
